using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;

public class WebSocketServerHelper : IDisposable
{
    public event Action<WebSocketClient> ClientConnected;
    public event Action<Guid> ClientDisconnected;

    private TcpListener _listener;
    private bool _isRunning;

    public void Stop()
    {
        _isRunning = false;
        _listener?.Stop();
        _heartbeatTimer?.Dispose();
        foreach (var client in _clients.Values)
        {
            client.Dispose();
        }
        _clients.Clear();
    }
    private readonly ConcurrentDictionary<Guid, WebSocketClient> _clients = new();
    private Timer _heartbeatTimer;
    private const int HeartbeatInterval = 30000; // 30秒心跳

    public void Start(int port)
    {
        _listener = new TcpListener(IPAddress.Any, port);
        _listener.Start();
        _heartbeatTimer = new Timer(CheckConnections, null, HeartbeatInterval, HeartbeatInterval);
        _ = AcceptClientsAsync();
    }

    private async Task AcceptClientsAsync()
    {
        while (true)
        {
            var client = await _listener.AcceptTcpClientAsync();
            var wsClient = new WebSocketClient(client);
            _clients.TryAdd(wsClient.Id, wsClient);
            ClientConnected?.Invoke(wsClient);
            _ = HandleClientAsync(wsClient);
        }
    }

    private async Task HandleClientAsync(WebSocketClient client)
    {
        try
        {
            using (client)
            using (var stream = client.TcpClient.GetStream())
            {
                // WebSocket握手协议
                var handshake = await ReadHandshake(stream);
                var response = CreateHandshakeResponse(handshake);
                await stream.WriteAsync(response, 0, response.Length);

                // WebSocket消息循环
                var buffer = new byte[1024];
                while (true)
                {
                    var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    if (bytesRead == 0) break;

                    // 解析WebSocket帧头
                    var fin = (buffer[0] & 0x80) != 0;
                    var opcode = buffer[0] & 0x0F;
                    var mask = (buffer[1] & 0x80) != 0;
                    var payloadLength = buffer[1] & 0x7F;

                    // 处理Ping帧
                    if (opcode == 0x9)
                    {
                        var pongFrame = CreatePongFrame(buffer, bytesRead);
                        await stream.WriteAsync(pongFrame, 0, pongFrame.Length);
                    }

                    client.LastActivity = DateTime.UtcNow;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"客户端连接错误: {ex.Message}");
        }
        finally
        {
            _clients.TryRemove(client.Id, out _);
            ClientDisconnected?.Invoke(client.Id);
        }
    }

    private void CheckConnections(object state)
    {
        var now = DateTime.UtcNow;
        foreach (var client in _clients.Values.ToArray())
        {
            try
            {
                if ((now - client.LastActivity).TotalSeconds > 60)
                {
                    client.Dispose();
                }
                else
                {
                    var stream = client.TcpClient.GetStream();
                    var pingFrame = CreatePingFrame();
                    stream.Write(pingFrame, 0, pingFrame.Length);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"心跳检测异常: {ex.Message}");
                client.Dispose();
            }
        }
    }

    private static byte[] CreatePingFrame()
    {
        return new byte[] { 0x89, 0x00 }; // FIN + Ping opcode
    }
    private static byte[] CreatePongFrame(byte[] pingData, int length)
    {
        var frame = new byte[length];
        frame[0] = 0x8A; // FIN + Pong opcode
        Array.Copy(pingData, 1, frame, 1, length - 1);
        return frame;
    }

    private async Task<byte[]> ReadHandshake(NetworkStream stream)
    {
        var buffer = new byte[1024];
        var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
        return buffer[..bytesRead];
    }

    private byte[] CreateHandshakeResponse(byte[] handshake)
    {
        var request = System.Text.Encoding.UTF8.GetString(handshake);
        var key = System.Text.RegularExpressions.Regex.Match(request, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim();
        var responseKey = Convert.ToBase64String(
            System.Security.Cryptography.SHA1.Create().ComputeHash(
                System.Text.Encoding.UTF8.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));

        return System.Text.Encoding.UTF8.GetBytes(
            "HTTP/1.1 101 Switching Protocols\r\n" +
            "Connection: Upgrade\r\n" +
            "Upgrade: websocket\r\n" +
            "Sec-WebSocket-Accept: " + responseKey + "\r\n\r\n");
    }

    public void Dispose()
    {
        Stop();
        _listener?.Stop();
        _heartbeatTimer?.Dispose();
        foreach (var client in _clients.Values)
        {
            client.Dispose();
        }
    }
}

public class WebSocketClient : IDisposable
{
    public Guid Id { get; } = Guid.NewGuid();
    public TcpClient TcpClient { get; }
    public DateTime LastActivity { get; set; } = DateTime.UtcNow;

    public WebSocketClient(TcpClient client)
    {
        TcpClient = client;
    }

    public void Dispose()
    {
       // Stop();
        TcpClient?.Dispose();
    }
}

